package cn.lingyangwl.framework.core.utils;

import cn.lingyangwl.framework.tool.core.exception.Assert;
import cn.lingyangwl.framework.tool.core.exception.UtilException;
import cn.lingyangwl.framework.tool.core.StringUtils;
import cn.lingyangwl.framework.tool.core.file.FileTypeUtils;
import cn.lingyangwl.framework.tool.core.file.FileUtils;
import cn.lingyangwl.framework.tool.core.file.MimeTypesUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Objects;

/**
 * 文件下载工具类
 *
 * @author shenguangyang
 */
@Slf4j
public class FileDownloadUtils {
    /**
     * buffer大小
     */
    private static Integer BUFFER_SIZE = 4096;

    public static Integer getBufferSize() {
        return BUFFER_SIZE;
    }

    public static void setBufferSize(Integer bufferSize) {
        BUFFER_SIZE = bufferSize;
    }

    /**
     * 下载文件
     *
     * @param fileName 浏览器下载文件时候, 展示的文件名, 如果为空则采用本地文件名
     * @param file     文件
     * @param response 响应
     */
    public static void download(File file, String fileName, HttpServletResponse response) throws IOException {
        Assert.notNull(file, "file == null");
        if (!file.exists()) {
            throw new UtilException("file not exist");
        }
        // 读到流中
        try (InputStream inputStream = Files.newInputStream(Paths.get(file.getAbsolutePath()))) {
            // 文件的存放路径
            response.reset();
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("utf-8");
            response.setContentLength((int) file.length());
            fileName = StringUtils.isEmpty(fileName) ? new File(file.getAbsolutePath()).getName() : fileName;
            response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
            ServletOutputStream outputStream = response.getOutputStream();
            byte[] b = new byte[BUFFER_SIZE];
            int len;
            // 从输入流中读取一定数量的字节，并将其存储在缓冲区字节数组中，读到末尾返回-1
            while ((len = inputStream.read(b)) > 0) {
                outputStream.write(b, 0, len);
                outputStream.flush();
            }
        }
    }

    /**
     * 下载文件
     *
     * @param fileName 浏览器下载文件时候, 展示的文件名, 如果为空则采用本地文件名
     * @param bytes    字节数组
     * @param response 响应
     */
    public static void download(byte[] bytes, String contentType, String fileName, HttpServletResponse response) throws IOException {
        if (Objects.isNull(bytes) || bytes.length == 0) {
            log.warn("download file bytes size 0, filename: {}, ", fileName);
            return;
        }
        try {
            response.reset();
            if (StringUtils.isEmpty(contentType)) {
                response.setContentType("application/octet-stream");
            } else {
                response.setContentType(contentType);
            }
            response.setCharacterEncoding("utf-8");
            response.setContentLength(bytes.length);
            response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
            ServletOutputStream outputStream = response.getOutputStream();
            outputStream.write(bytes);
            outputStream.flush();
        } finally {

        }
    }


    /**
     * 下载文件
     *
     * @param fileName 浏览器下载文件时候, 展示的文件名, 如果为空则采用本地文件名
     * @param inputStream  输入流
     * @param response 响应
     */
    public static void download(InputStream inputStream, long length, String contentType,
                                String fileName, boolean isAutoClose,
                                HttpServletResponse response) throws IOException {
        try {
            response.reset();
            if (StringUtils.isEmpty(contentType)) {
                response.setContentType("application/octet-stream");
            } else {
                response.setContentType(contentType);
            }

            response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
            response.setCharacterEncoding("utf-8");
            response.setContentLength((int) length);
            ServletOutputStream outputStream = response.getOutputStream();
            byte[] b = new byte[BUFFER_SIZE];
            int len;
            // 从输入流中读取一定数量的字节，并将其存储在缓冲区字节数组中，读到末尾返回-1
            while ((len = inputStream.read(b)) > 0) {
                outputStream.write(b, 0, len);
                outputStream.flush();
            }
        } finally {
            if (isAutoClose && Objects.nonNull(inputStream)) {
                inputStream.close();
            }
        }
    }

    /**
     * 检查文件是否可下载
     *
     * @param resource 需要下载的文件
     * @return true 正常 false 非法
     */
    public static boolean checkAllowDownload(String resource) {
        // 禁止目录上跳级别
        if (StringUtils.contains(resource, "..")) {
            return false;
        }

        // 检查允许下载的文件规则
        return ArrayUtils.contains(MimeTypesUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource));
        // 不在允许下载的文件规则
    }

    /**
     * 下载文件名重新编码
     *
     * @param request  请求对象
     * @param fileName 文件名
     * @return 编码后的文件名
     */
    public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
        final String agent = request.getHeader("USER-AGENT");
        String filename = fileName;
        if (agent.contains("MSIE")) {
            // IE浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+", " ");
        } else if (agent.contains("Firefox")) {
            // 火狐浏览器
            filename = new String(fileName.getBytes(), "ISO8859-1");
        } else if (agent.contains("Chrome")) {
            // google浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        } else {
            // 其它浏览器
            filename = URLEncoder.encode(filename, "utf-8");
        }
        return filename;
    }

    /**
     * 下载文件名重新编码
     *
     * @param response     响应对象
     * @param realFileName 真实文件名
     * @throws UnsupportedEncodingException 异常
     */
    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
        String percentEncodedFileName = FileUtils.percentEncode(realFileName);

        StringBuilder contentDispositionValue = new StringBuilder();
        contentDispositionValue.append("attachment; filename=")
                .append(percentEncodedFileName)
                .append(";")
                .append("filename*=")
                .append("utf-8''")
                .append(percentEncodedFileName);

        response.setHeader("Content-disposition", contentDispositionValue.toString());
    }
}
